/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */

/* vsscanf, vswscanf, vfscanf, and vfwscanf all come here for i386 and arm.

   The goal of this routine is to turn a call to v*scanf into a call to
   s*scanf.  This is needed because mingw-w64 uses msvcr100.dll, which doesn't
   support the v*scanf functions instead of msvcr120.dll which does.
   Unfortunately, there is no defined way to know exactly how big a va_list
   is, so we use a hard-coded buffer.

   I suppose a sufficiently-motivated person could try to parse the format
   to figure out how many tokens there are... */

/* The function prototype here is (essentially):

   int __ms_vsscanf_internal (void *s,
			     void *format,
			     void *arg,
			     void *func);

   I say 'essentially' because passing a function pointer as void in ISO
   is not supported.  But in the end, I take the first parameter (which
   may be a char *, a wchar_t *, or a FILE *) and put it into the newly
   formed stack, and eventually call the address in func.  */

#if defined (__x86_64__)

    .text
    .align 16

    /* scl   2: C_EXT  - External (public) symbol - covers globals and externs
       type 32: DT_FCN - function returning T
	 */
    .def __argtos;    .scl    2;    .type    32;    .endef

    /* The max number of pointers we support.  Must be an even number
       to keep the 64bit stack 16byte aligned.  Must not be less than 4.  */
    .equ entries, 30

    /* 64bit pointers are 8 bytes.  */
    .equ sizeof, 8

    /* Size of our buffer.  */
    .equ iBytes, entries * sizeof

    /* Stack space for first 2 args to s*scanf.  */
    .equ iOffset, (2 * sizeof)

    .seh_proc __argtos
__argtos:

    /* When we are done:
      - s must be in rcx.  That's where it is on entry.
      - format must be in rdx.  That's where it is on entry.
      - The first pointer in arg must be in r8. arg is in r8 on entry.
      - The second pointer in arg must be in r9. arg is in r8 on entry.
      - The ($entries - 2) other pointers in arg must be on the stack,
	starting 32bytes into rsp.  */

    /* We need enough room to shadow (s + format)
       + (enough room for all the other args).  */
    subq $(iOffset + iBytes), %rsp
    .seh_stackalloc iOffset + iBytes

    .seh_endprologue

    /* We are going to copy $entries pointers from arg to our
       local stack.  Except the first 2, since they will be
       loaded in registers.  */
    movq $entries - 2, %r10 /* # of ptrs to copy.  */

    /* The first 32 bytes are in registers, but by spec, space
	   must still be reserved for them on the stack.  Put the
       rest of the pointers in the stack after that.  */
    lea 32(%rsp), %r11 /* dst.  */

.LOOP:
    subq $1, %r10

    /* Use 16 to skip over the first 2 pointers.  */
    movq 16(%r8, %r10, 8), %rax
    movq %rax, (%r11, %r10, 8)
    jnz .LOOP

    /* r9 contains the routine we are going to call.  Since we are about to
       overwrite it, move it somewhere safe.  */
    movq %r9, %r10

    /* The stack is now correctly populated, and so are rcx and rdx.
       But we need to load the last 2 regs before making the call.  */
    movq 0x8(%r8), %r9 /* 2nd dest location (may be garbage if only 1 arg).  */
    movq (%r8), %r8 /* 1st dest location.  */

    /* Make the call.  */
    callq *%r10

    addq $(iOffset + iBytes), %rsp

    retq
    .seh_endproc

#elif defined (_X86_)

    .text
    .align 16

    /* scl   2: C_EXT  - External (public) symbol - covers globals and externs
       type 32: DT_FCN - function returning T
	 */
    .def __argtos;    .scl    2;    .type    32;    .endef

    /* The max number of pointers we support.  Must not be less than 1.  */
    .equ entries, 30

    /* 64bit pointers are 8 bytes.  */
    .equ sizeof, 4

    /* Size of our buffer.  */
    .set iBytes, entries * sizeof

    /* Stack space for first 2 args to s*scanf.  */
    .equ iOffset, (2 * sizeof)

__argtos:
    pushl %ebp
    movl %esp, %ebp
    pushl %edi

    /* Reserve enough stack space for everything.

       Stack usage will look like:
       4 bytes - s
       4 bytes - format
       (iBytes) bytes - variable # of parameters for sscanf (all ptrs).  */

    subl $(iOffset + iBytes), %esp

    /* Write out s and format where they need to be for the sscanf call.  */
    movl 8(%ebp), %eax
    movl %eax, (%esp)  /* s.  */
    movl 12(%ebp), %edx
    movl %edx, 0x4(%esp)  /* format.  */

    /* We are going to copy $entries pointers from arg to our
       local stack.  */
    movl $entries, %ecx /* # of ptrs to copy.  */
    lea iOffset(%esp), %edi /* dst.  */
    movl 16(%ebp), %edx /* src.  */

.LOOP:
    subl $1, %ecx

    movl (%edx, %ecx, 4), %eax
    movl %eax, (%edi, %ecx, 4)
    jnz .LOOP

    /* The stack is now correctly populated.  */

    /* Make the call.  */
    call *20(%ebp)

    /* Restore stack.  */
    popl %edi
    leave

    ret

#elif defined (__arm__)

    .text
    .align 2
    .thumb_func
    .globl __argtos

__argtos:
    push    {r4-r7, lr}
    sub     sp, sp, #128
    mov     r12, r3
    mov     r4, sp

    ldr     r5, [r2], #4
    ldr     r6, [r2], #4

    mov     r3, #116
1:  ldr     r7, [r2], #4
    str     r7, [r4], #4
    subs    r3, r3, #4
    bne     1b

    mov     r2, r5
    mov     r3, r6
    blx     r12
    add     sp, sp, #128
    pop     {r4-r7, pc}

#elif defined (__aarch64__)

    .text
    .align 2
    .globl __argtos

__argtos:
    stp     x29, x30, [sp, #-16]!
    mov     x29, sp
    sub     sp, sp, #256
    mov     x9, sp
    mov     x10, x2
    mov     x11, x3

    ldr     x2, [x10], #8
    ldr     x3, [x10], #8
    ldr     x4, [x10], #8
    ldr     x5, [x10], #8
    ldr     x6, [x10], #8
    ldr     x7, [x10], #8

    mov     x12, #240
1:  ldr     x13, [x10], #8
    str     x13, [x9], #8
    subs    x12, x12, #8
    b.ne    1b

    blr     x11
    mov     sp, x29
    ldp     x29, x30, [sp], #16
    ret

#endif
